﻿using System;
using System.Collections.Specialized;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Security;
using System.Text;
using System.Threading;

namespace TigerCapture.Utils
{
    /// <summary>
    /// HTTP 客户端辅助类
    /// </summary>
    public class HttpHelper
    {
        private const int WebRequestTryCount = 3;
        private const int WebRequestTryInterval = 3000;

        /// <summary>
        /// 文件缓冲区大小
        /// </summary>
        public static int BufferSize = 2048;

        /// <summary>
        /// 上传文件超时时限
        /// </summary>
        public static int UploadFileTimeout = Timeout.Infinite;

        /// <summary>
        /// 请求接口数据超时时限
        /// </summary>
        public static int RequestTimeout = 300000;

        /// <summary>
        /// Web代理
        /// </summary>
        public static IWebProxy WebProxy = null;

        /// <summary>
        /// 是否使用Https访问服务器
        /// </summary>
        public static bool UseHttps = false;

        static HttpHelper()
        {
            // 验证服务器证书回调
            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback((o, e, h, s) =>
            {
                // 自动验证
                return true;
            });
        }

        /// <summary>
        /// 设置Web代理
        /// </summary>
        /// <param name="httpWebRequest"></param>
        private static void SetWebProxy(HttpWebRequest httpWebRequest)
        {
            if (WebProxy != null)
            {
                httpWebRequest.Proxy = WebProxy;
            }
        }

        /// <summary>
        /// 设置Web代理
        /// </summary>
        /// <param name="webClient"></param>
        private static void SetWebProxy(WebClient webClient)
        {
            if (WebProxy != null)
            {
                webClient.Proxy = WebProxy;
            }
        }

        /// <summary>
        /// 反转Uri的架构
        /// </summary>
        /// <param name="httpUri"></param>
        /// <returns></returns>
        public static Uri ReverseHttpUriScheme(Uri httpUri)
        {
            if (httpUri == null)
            {
                throw new ArgumentNullException("httpUri");
            }

            if (httpUri.Scheme != Uri.UriSchemeHttp &&
                httpUri.Scheme != Uri.UriSchemeHttps)
            {
                throw new ArgumentException("Invalid httpUri.");
            }

            string tempUrl;

            if (httpUri.Scheme == Uri.UriSchemeHttps)
            {
                tempUrl = Uri.UriSchemeHttp + httpUri.AbsoluteUri.Substring(Uri.UriSchemeHttps.Length);
            }
            else
            {
                tempUrl = Uri.UriSchemeHttps + httpUri.AbsoluteUri.Substring(Uri.UriSchemeHttp.Length);
            }

            return new Uri(tempUrl);
        }

        /// <summary>
        /// 验证请求路径
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="message"></param>
        private static void ValidateRemoteUrl(ref Uri uri, string message)
        {
            if (uri.Scheme != Uri.UriSchemeHttp &&
                uri.Scheme != Uri.UriSchemeHttps)
            {
                throw new ArgumentException(message);
            }

            if ((UseHttps && uri.Scheme == Uri.UriSchemeHttp) ||
                (!UseHttps && uri.Scheme == Uri.UriSchemeHttps))
            {
                uri = ReverseHttpUriScheme(uri);
            }
        }

        /// <summary>
        /// 获取要下载文件的大小
        /// </summary>
        /// <param name="remoteFileUrl">远程 HTTP 服务器文件路径</param>
        /// <returns></returns>
        public static long GetDownloadFileSize(string remoteFileUrl)
        {
            var uri = new Uri(remoteFileUrl);

            ValidateRemoteUrl(ref uri, "remoteFileUrl");

            var request = (HttpWebRequest)WebRequest.Create(uri);

            SetWebProxy(request);

            using (var response = (HttpWebResponse)request.GetResponse())
            {
                return response.ContentLength;
            }
        }

        /// <summary>
        /// 从远程 HTTP 服务器下载文件到本地
        /// </summary>
        /// <param name="remoteFileUrl">远程 HTTP 服务器文件路径</param>
        /// <param name="localFilePath">本地文件路径，若文件已存在将被覆盖</param>
        /// <param name="tryCount">尝试次数（默认3次）</param>
        /// <param name="tryInterval">尝试间隔（默认3秒）</param>
        public static void DownloadFile
        (
            string remoteFileUrl,
            string localFilePath,
            int tryCount = WebRequestTryCount,
            int tryInterval = WebRequestTryInterval
        )
        {
            var uri = new Uri(remoteFileUrl);

            ValidateRemoteUrl(ref uri, "remoteFileUrl");

            if (string.IsNullOrWhiteSpace(localFilePath))
            {
                throw new ArgumentNullException("localFilePath");
            }

            if (tryCount < 0)
            {
                tryCount = 0;
            }

            if (tryInterval < 0)
            {
                tryInterval = WebRequestTryInterval;
            }

            var count = 0;

            Label0:

            try
            {
                var request = (HttpWebRequest)WebRequest.Create(uri);

                SetWebProxy(request);

                using (var response = (HttpWebResponse)request.GetResponse())
                using (var stream = response.GetResponseStream())
                using (var fs = new FileStream(localFilePath, FileMode.Create, FileAccess.Write))
                {
                    var buffer = new byte[BufferSize];

                    int readedLength;

                    while ((readedLength = stream.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        fs.Write(buffer, 0, readedLength);
                    }
                }
            }
            catch (WebException ex)
            {
                if (++count <= tryCount)
                {
                    Thread.Sleep(tryInterval);

                    goto Label0;
                }

                throw ex;
            }
        }

        /// <summary>
        /// 将本地文件上传到远程 HTTP 服务器
        /// </summary>
        /// <param name="localFilePath">本地文件路径</param>
        /// <param name="remoteFileUrl">远程 HTTP 服务器文件路径</param>
        /// <param name="name">表单名称</param>
        /// <param name="fileName">表单中文件名称</param>
        /// <param name="headerNameValues">请求头中的附加参数</param>
        /// <param name="progressCallback">上传进度的回调方法</param>
        /// <param name="responseInfo">服务器返回的结果信息</param>
        /// <returns></returns>
        public static bool UploadFile
        (
            string localFilePath,
            string remoteFileUrl,
            string name,
            string fileName,
            NameValueCollection headerNameValues,
            Action<double> progressCallback,
            out string responseInfo
        )
        {
            var uri = new Uri(remoteFileUrl);

            ValidateRemoteUrl(ref uri, "remoteFileUrl");

            return UploadFile(uri, localFilePath, name, fileName, headerNameValues, progressCallback, out responseInfo);
        }

        /// <summary>
        /// 将本地文件上传到远程 HTTP 服务器，若上传失败按照指定的间隔时间尝试重新上传指定次数
        /// </summary>
        /// <param name="localFilePath">本地文件路径</param>
        /// <param name="remoteFileUrl">远程 HTTP 服务器文件路径</param>
        /// <param name="name">表单名称</param>
        /// <param name="fileName">表单中文件名称</param>
        /// <param name="headerNameValues">请求头中的附加参数</param>
        /// <param name="tryCount">失败后尝试重新上传的次数</param>
        /// <param name="tryInterval">失败后尝试重新上传的间隔毫秒数</param>
        /// <param name="progressCallback">上传进度的回调方法</param>
        /// <param name="responseInfo">服务器返回的结果信息</param>
        /// <returns></returns>
        public static bool UploadFile
        (
            string localFilePath,
            string remoteFileUrl,
            string name,
            string fileName,
            NameValueCollection headerNameValues,
            int tryCount,
            int tryInterval,
            Action<double> progressCallback,
            out string responseInfo
        )
        {
            var uri = new Uri(remoteFileUrl);

            ValidateRemoteUrl(ref uri, "remoteFileUrl");

            if (tryCount < 0)
            {
                tryCount = 0;
            }

            if (tryInterval < 0)
            {
                tryInterval = WebRequestTryInterval;
            }

            var count = 0;

            Label0:

            try
            {
                return UploadFile(uri, localFilePath, name, fileName, headerNameValues, progressCallback, out responseInfo);
            }
            catch (WebException ex)
            {
                if (++count <= tryCount)
                {
                    Thread.Sleep(tryInterval);

                    goto Label0;
                }

                throw ex;
            }
        }

        private static bool UploadFile
        (
            Uri uri,
            string localFilePath,
            string name,
            string fileName,
            NameValueCollection headerNameValues,
            Action<double> progressCallback,
            out string responseInfo
        )
        {
            if (string.IsNullOrWhiteSpace(localFilePath))
            {
                throw new ArgumentNullException("localFilePath");
            }

            if (string.IsNullOrWhiteSpace(name))
            {
                throw new ArgumentNullException("name");
            }

            if (string.IsNullOrWhiteSpace(fileName))
            {
                throw new ArgumentNullException("fileName");
            }

            responseInfo = string.Empty;

            //时间戳   
            var boundary = DateTime.Now.Ticks.ToString("X");

            var beginBoundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");

            var endBoundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");

            //请求头部信息   
            var headers = new StringBuilder("Content-Disposition:form-data; ");

            headers.Append("name=\"");
            headers.Append(name);
            headers.Append("\"; filename=\"");
            headers.Append(fileName);
            headers.Append("\"\r\n");
            headers.Append("Content-Type:application/octet-stream\r\n\r\n");

            var postHeaderBytes = Encoding.UTF8.GetBytes(headers.ToString());

            FileStream fileStream = null;

            try
            {
                fileStream = new FileStream(localFilePath, FileMode.Open, FileAccess.Read);

                var request = (HttpWebRequest)WebRequest.Create(uri);

                SetWebProxy(request);

                if (headerNameValues != null)
                {
                    foreach (var key in headerNameValues.AllKeys)
                    {
                        request.Headers.Add(key, headerNameValues[key]);
                    }
                }

                request.Method = WebRequestMethods.Http.Post;
                request.Timeout = UploadFileTimeout;
                request.AllowWriteStreamBuffering = false;
                request.KeepAlive = true;
                request.Credentials = CredentialCache.DefaultCredentials;
                request.ContentType = "multipart/form-data;charset=utf-8;boundary=" + boundary;
                request.ContentLength = fileStream.Length + postHeaderBytes.Length + beginBoundaryBytes.Length + endBoundaryBytes.Length;

                using (var postStream = request.GetRequestStream())
                {
                    postStream.Write(beginBoundaryBytes, 0, beginBoundaryBytes.Length);

                    postStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);

                    fileStream.Position = 0;

                    var buffer = new byte[BufferSize];

                    int readedLength;

                    var totalLength = request.ContentLength;

                    var readedCount = 0;

                    while ((readedLength = fileStream.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        postStream.Write(buffer, 0, readedLength);

                        if (progressCallback != null)
                        {
                            readedCount += readedLength;

                            progressCallback.BeginInvoke(Math.Round((readedCount * 100.0 / totalLength)), null, null);
                        }
                    }

                    if (progressCallback != null)
                    {
                        progressCallback.BeginInvoke(100, null, null);
                    }

                    postStream.Write(endBoundaryBytes, 0, endBoundaryBytes.Length);
                }

                using (var response = (HttpWebResponse)request.GetResponse())
                {
                    if (response.StatusCode == HttpStatusCode.OK)
                    {
                        using (var responseStream = response.GetResponseStream())
                        using (var streamReader = new StreamReader(responseStream))
                        {
                            responseInfo = streamReader.ReadToEnd();
                        }

                        return true;
                    }

                    return false;
                }
            }
            finally
            {
                if (fileStream != null)
                {
                    fileStream.Close();
                }
            }
        }

        /// <summary>
        /// 向远程 HTTP 服务器请求结果
        /// </summary>
        /// <param name="requestUrl">请求结果的 Url</param>
        /// <returns></returns>
        public static string RequestResult(string requestUrl, bool decompress = false, bool expect100Continue = true, string method = WebRequestMethods.Http.Get)
        {
            var uri = new Uri(requestUrl);

            ValidateRemoteUrl(ref uri, "requestUrl");

            try
            {
                return RequestResult(uri, decompress, expect100Continue, method);
            }
            catch (WebException ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 向远程 HTTP 服务器请求结果，若请求失败按照指定的间隔时间尝试重新上传指定次数。
        /// </summary>
        /// <param name="requestUrl">请求结果的 Url。</param>
        /// <param name="tryCount">失败后尝试重新上传的次数。</param>
        /// <param name="tryInterval">失败后尝试重新上传的间隔毫秒数。</param>
        /// <param name="decompress">是否压缩。</param>
        /// <param name="expect100Continue">是否使用100-Continue行为。</param>
        /// <param name="method">请求方法。</param>
        /// <returns>请求结果。</returns>
        public static string RequestResult(string requestUrl, int tryCount, int tryInterval, bool decompress = false, bool expect100Continue = true, string method = WebRequestMethods.Http.Get)
        {
            var uri = new Uri(requestUrl);

            ValidateRemoteUrl(ref uri, "requestUrl");

            if (tryCount < 0)
            {
                tryCount = 0;
            }

            if (tryInterval < 0)
            {
                tryInterval = WebRequestTryInterval;
            }

            var count = 0;

            Label0:

            try
            {
                return RequestResult(uri, decompress, expect100Continue, method);
            }
            catch (WebException ex)
            {
                if (++count <= tryCount)
                {
                    Thread.Sleep(tryInterval);

                    goto Label0;
                }

                throw ex;
            }
        }

        private static string RequestResult(Uri uri, bool decompress, bool expect100Continue, string method)
        {
            var request = (HttpWebRequest)WebRequest.Create(uri);

            SetWebProxy(request);

            request.ServicePoint.Expect100Continue = expect100Continue;
            request.Timeout = RequestTimeout;
            request.Method = method;

            if (decompress)
            {
                request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
            }

            using (var response = (HttpWebResponse)request.GetResponse())
            using (var stream = response.GetResponseStream())
            using (var reader = new StreamReader(stream))
            {
                return reader.ReadToEnd();
            }
        }

        /// <summary>
        /// 以 HTTP POST 方式上传键值数据
        /// </summary>
        /// <param name="requestUrl">上传数据的服务器路径</param>
        /// <param name="values">键值数据集合</param>
        /// <returns></returns>
        public static string PostData(string requestUrl, NameValueCollection values)
        {
            var uri = new Uri(requestUrl);

            ValidateRemoteUrl(ref uri, "requestUrl");

            if (values == null)
            {
                throw new ArgumentNullException("values");
            }

            try
            {
                return PostData(values, uri);
            }
            catch (WebException ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 以 HTTP POST 方式上传键值数据，若上传失败按照指定的间隔时间尝试重新上传指定次数
        /// </summary>
        /// <param name="requestUrl">上传数据的服务器路径</param>
        /// <param name="values">键值数据集合</param>
        /// <param name="tryCount">失败后尝试重新上传的次数</param>
        /// <param name="tryInterval">失败后尝试重新上传的间隔毫秒数</param>
        /// <returns></returns>
        public static string PostData(string requestUrl, NameValueCollection values, int tryCount, int tryInterval)
        {
            var uri = new Uri(requestUrl);

            ValidateRemoteUrl(ref uri, "requestUrl");

            if (values == null)
            {
                throw new ArgumentNullException("values");
            }

            if (tryCount < 0)
            {
                tryCount = 0;
            }

            if (tryInterval < 0)
            {
                tryInterval = WebRequestTryInterval;
            }

            var count = 0;

            Label0:

            try
            {
                return PostData(values, uri);
            }
            catch (WebException ex)
            {
                if (++count <= tryCount)
                {
                    Thread.Sleep(tryInterval);

                    goto Label0;
                }

                throw ex;
            }
        }

        private static string PostData(NameValueCollection values, Uri uri)
        {
            using (var client = new WebClient())
            {
                SetWebProxy(client);

                var response = client.UploadValues(uri, WebRequestMethods.Http.Post, values);

                return Encoding.UTF8.GetString(response);
            }
        }

        /// <summary>
        /// 以 HTTP POST 方式上传字符数据
        /// </summary>
        /// <param name="requestUrl">上传数据的服务器路径</param>
        /// <param name="content">字符数据</param>
        /// <param name="compress">是否压缩字符数据</param>
        /// <returns></returns>
        public static string PostData(string requestUrl, string content, bool compress = false)
        {
            var uri = new Uri(requestUrl);

            ValidateRemoteUrl(ref uri, "requestUrl");

            if (string.IsNullOrWhiteSpace(content))
            {
                throw new ArgumentNullException("content");
            }

            try
            {
                return PostData(content, uri, compress);
            }
            catch (WebException ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 以 HTTP POST 方式上传字符数据，若上传失败按照指定的间隔时间尝试重新上传指定次数
        /// </summary>
        /// <param name="requestUrl">上传数据的服务器路径</param>
        /// <param name="content">字符数据</param>
        /// <param name="tryCount">失败后尝试重新上传的次数</param>
        /// <param name="tryInterval">失败后尝试重新上传的间隔毫秒数</param>
        /// <param name="compress">是否压缩字符数据</param>
        /// <returns></returns>
        public static string PostData(string requestUrl, string content, int tryCount, int tryInterval, bool compress = false)
        {
            var uri = new Uri(requestUrl);

            ValidateRemoteUrl(ref uri, "requestUrl");

            if (string.IsNullOrWhiteSpace(content))
            {
                throw new ArgumentNullException("content");
            }

            if (tryCount < 0)
            {
                tryCount = 0;
            }

            if (tryInterval < 0)
            {
                tryInterval = WebRequestTryInterval;
            }

            var count = 0;

            Label0:

            try
            {
                return PostData(content, uri, compress);
            }
            catch (WebException ex)
            {
                if (++count <= tryCount)
                {
                    Thread.Sleep(tryInterval);

                    goto Label0;
                }

                throw ex;
            }
        }

        private static string PostData(string content, Uri uri, bool compress)
        {
            using (var client = new WebClient())
            {
                SetWebProxy(client);

                if (compress)
                {
                    client.Headers.Add("Content-Encoding", "gzip");

                    var response = client.UploadData(uri, WebRequestMethods.Http.Post, GZipBytes(content));

                    return Encoding.UTF8.GetString(response);
                }
                else
                {
                    return client.UploadString(uri, WebRequestMethods.Http.Post, content);
                }
            }
        }

        private static byte[] GZipBytes(string data)
        {
            byte[] ret = null;

            using (var outputStream = new MemoryStream())
            {
                using (var gzip = new GZipStream(outputStream, CompressionMode.Compress))
                {
                    using (var writer = new StreamWriter(gzip))
                    {
                        writer.Write(data);
                        writer.Flush();
                    }

                    gzip.Close();

                    ret = outputStream.ToArray();
                }
            }

            return ret;
        }

        /// <summary>
        /// 将带有Http协议的path1与不带有Http协议的path2合并成一条路径
        /// </summary>
        /// <param name="path1">带有Http协议的路径</param>
        /// <param name="path2">不带有Http协议的路径</param>
        /// <returns></returns>
        public static string Combine(string path1, string path2)
        {
            if (string.IsNullOrWhiteSpace(path1) ||
                string.IsNullOrWhiteSpace(path2))
            {
                throw new ArgumentNullException();
            }

            var uri = new Uri(path1);

            if (uri.Scheme != Uri.UriSchemeHttp &&
                uri.Scheme != Uri.UriSchemeHttps)
            {
                throw new ArgumentException("path1");
            }

            return string.Format(@"{0}/{1}", path1.TrimEnd('/'), path2.TrimStart('/'));
        }

        /// <summary>
        /// 以字符串的形式下载请求的资源。
        /// </summary>
        /// <param name="uri">资源地址。</param>
        /// <param name="timeout">超时。</param>
        /// <returns>请求结果。</returns>
        public static string DownloadString(string uri, int timeout = 1000)
        {
            var request = (HttpWebRequest)WebRequest.Create(uri);

            SetWebProxy(request);

            request.Timeout = timeout;
            request.Method = WebRequestMethods.Http.Get;

            using (var response = (HttpWebResponse)request.GetResponse())
            using (var stream = response.GetResponseStream())
            using (var reader = new StreamReader(stream))
            {
                return reader.ReadToEnd();
            }
        }

        public class AsyncUploadFile
        {
            private int _bufferSize = 2048;
            private int _uploadFileTimeout = Timeout.Infinite;
            private string _boundary;
            private byte[] _beginBoundaryBytes;
            private byte[] _endBoundaryBytes;
            private byte[] _headerBytes;
            private FileStream _uploadFileStream;
            private Stream _httpRequestStream;
            private HttpWebResponse _httpResponse;
            private IAsyncResult _requestStreamAsyncStream;
            private IAsyncResult _responseAsyncStream;
            private string _name;
            private string _fileName;
            private NameValueCollection _headerNameValues;

            private bool _isTry;
            private int _tryCount = 3;
            private int _tryInterval = 1000;

            private int _triedCount;

            /// <summary>
            /// 获取或设置一个值，该值表示是否在传输失败时尝试重新上传
            /// </summary>
            public bool IsTry
            {
                get { return _isTry; }
                set { _isTry = value; }
            }

            /// <summary>
            /// 获取或设置传输失败时尝试重新上传的次数（若为 -1 表示一直重新上传）
            /// </summary>
            public int TryCount
            {
                get { return _tryCount; }
                set
                {
                    if (value < 0)
                    {
                        throw new ArgumentException();
                    }

                    _tryCount = value;
                }
            }

            /// <summary>
            /// 获取或设置传输失败时尝试重新上传的间隔毫秒数（默认1000毫秒）
            /// </summary>
            public int TryInterval
            {
                get { return _tryInterval; }
                set
                {
                    if (value < 0)
                    {
                        throw new ArgumentException();
                    }

                    _tryInterval = value;
                }
            }

            /// <summary>
            /// 获取或设置写入流的缓冲区大小（默认 2048 KB）
            /// </summary>
            public int BufferSize
            {
                get { return _bufferSize; }
                set { _bufferSize = value; }
            }

            /// <summary>
            /// 获取或设置上传文件超时时限（默认 Timeout.Infinite）
            /// </summary>
            public int UploadFileTimeout
            {
                get { return _uploadFileTimeout; }
                set { _uploadFileTimeout = value; }
            }

            private Uri RemoteFileUri { get; set; }

            /// <summary>
            /// 获取正在上传的本地文件路径
            /// </summary>
            public string LocalFilePath { get; private set; }

            /// <summary>
            /// 获取上传结束后服务器的返回
            /// </summary>
            public string ResponseInfo { get; private set; }

            /// <summary>
            /// 上传结束服务器有返回时发生。
            /// </summary>
            public event EventHandler Uploaded;

            /// <summary>
            /// 上传过程中发生错误等情况时发生。
            /// </summary>
            public event EventHandler Aborted;

            /// <summary>
            /// 主动取消上传后发生。
            /// </summary>
            public event EventHandler Canceled;

            public AsyncUploadFile()
            {
                _boundary = DateTime.Now.Ticks.ToString("X");

                _beginBoundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + _boundary + "\r\n");

                _endBoundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + _boundary + "--\r\n");
            }

            /// <summary>
            /// 将本地文件以异步方式上传到远程 HTTP 服务器
            /// </summary>
            /// <param name="localFilePath">本地文件路径</param>
            /// <param name="remoteFileUrl">远程 HTTP 服务器文件路径</param>
            /// <param name="headerNameValues">文件传输的头信息</param>
            /// <returns></returns>
            public void Upload
            (
                string localFilePath,
                string remoteFileUrl,
                string name,
                string fileName,
                NameValueCollection headerNameValues
            )
            {
                if (string.IsNullOrWhiteSpace(localFilePath))
                {
                    throw new ArgumentNullException("localFilePath");
                }

                if (string.IsNullOrWhiteSpace(remoteFileUrl))
                {
                    throw new ArgumentNullException("remoteFileUrl");
                }

                if (string.IsNullOrWhiteSpace(name))
                {
                    throw new ArgumentNullException("name");
                }

                if (string.IsNullOrWhiteSpace(fileName))
                {
                    throw new ArgumentNullException("fileName");
                }

                var uri = new Uri(remoteFileUrl);

                ValidateRemoteUrl(ref uri, "remoteFileUrl");

                RemoteFileUri = uri;

                LocalFilePath = localFilePath;

                _name = name;
                _fileName = fileName;
                _headerNameValues = headerNameValues;
                _triedCount = 0;

                AsyncUpload(RemoteFileUri, LocalFilePath);
            }

            /// <summary>
            /// 取消异步上传文件
            /// </summary>
            public void Cancel()
            {
                try
                {
                    IsTry = false;

                    if (_requestStreamAsyncStream != null)
                    {
                        _requestStreamAsyncStream.AsyncWaitHandle.Close();

                        _requestStreamAsyncStream = null;
                    }
                    if (_responseAsyncStream != null)
                    {
                        _responseAsyncStream.AsyncWaitHandle.Close();

                        _responseAsyncStream = null;
                    }
                    if (_httpResponse != null)
                    {
                        _httpResponse.Close();

                        _httpResponse = null;
                    }
                    if (_httpRequestStream != null)
                    {
                        _httpRequestStream.Close();

                        _httpRequestStream = null;
                    }
                    if (_uploadFileStream != null)
                    {
                        _uploadFileStream.Close();

                        _uploadFileStream = null;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }

                if (Canceled != null)
                {
                    Canceled(this, EventArgs.Empty);
                }
            }

            private void AsyncUpload(Uri uri, string localFilePath)
            {
                try
                {
                    var headers = new StringBuilder("Content-Disposition:form-data; ");

                    headers.Append("name=\"");
                    headers.Append(_name);
                    headers.Append("\"; filename=\"");
                    headers.Append(_fileName);
                    headers.Append("\"\r\n");
                    headers.Append("Content-Type:application/octet-stream\r\n\r\n");

                    _headerBytes = Encoding.UTF8.GetBytes(headers.ToString());

                    _uploadFileStream = new FileStream(localFilePath, FileMode.Open, FileAccess.Read);

                    var request = (HttpWebRequest)WebRequest.Create(uri);

                    SetWebProxy(request);

                    if (_headerNameValues != null)
                    {
                        foreach (var key in _headerNameValues.AllKeys)
                        {
                            request.Headers.Add(key, _headerNameValues[key]);
                        }
                    }

                    request.Method = WebRequestMethods.Http.Post;
                    request.Timeout = UploadFileTimeout;
                    //request.AllowWriteStreamBuffering = false;
                    request.KeepAlive = true;
                    request.Credentials = CredentialCache.DefaultCredentials;
                    request.ContentType = "multipart/form-data;charset=utf-8;boundary=" + _boundary;
                    //request.ContentLength = _uploadFileStream.Length + _headerBytes.Length + _beginBoundaryBytes.Length + _endBoundaryBytes.Length;

                    _requestStreamAsyncStream = request.BeginGetRequestStream(GetRequestStreamCallback, request);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);

                    if (Aborted != null)
                    {
                        Aborted(this, EventArgs.Empty);
                    }
                }
            }

            private void GetRequestStreamCallback(IAsyncResult ar)
            {
                try
                {
                    var request = (HttpWebRequest)ar.AsyncState;

                    _httpRequestStream = request.EndGetRequestStream(ar);

                    _httpRequestStream.Write(_beginBoundaryBytes, 0, _beginBoundaryBytes.Length);

                    _httpRequestStream.Write(_headerBytes, 0, _headerBytes.Length);

                    _uploadFileStream.Position = 0;

                    var buffer = new byte[BufferSize];

                    int readedLength;

                    while ((readedLength = _uploadFileStream.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        _httpRequestStream.Write(buffer, 0, readedLength);
                    }

                    _uploadFileStream.Close();

                    _uploadFileStream = null;

                    _httpRequestStream.Write(_endBoundaryBytes, 0, _endBoundaryBytes.Length);

                    _httpRequestStream.Close();

                    _httpRequestStream = null;

                    _responseAsyncStream = request.BeginGetResponse(GetResponseCallback, request);
                }
                catch (Exception ex)
                {
                    if (ex is WebException)
                    {
                        if (IsTry && ++_triedCount <= TryCount)
                        {
                            Thread.Sleep(TryInterval);

                            AsyncUpload(RemoteFileUri, LocalFilePath);

                            return;
                        }
                    }

                    Console.WriteLine(ex);

                    if (Aborted != null)
                    {
                        Aborted(this, EventArgs.Empty);
                    }
                }
            }

            private void GetResponseCallback(IAsyncResult ar)
            {
                try
                {
                    var request = (HttpWebRequest)ar.AsyncState;

                    _httpResponse = (HttpWebResponse)request.EndGetResponse(ar);

                    if (_httpResponse.StatusCode == HttpStatusCode.OK)
                    {
                        using (var responseStream = _httpResponse.GetResponseStream())
                        using (var streamReader = new StreamReader(responseStream))
                        {
                            ResponseInfo = streamReader.ReadToEnd();
                        }
                    }

                    _httpResponse.Close();

                    _httpResponse = null;

                    if (Uploaded != null)
                    {
                        Uploaded(this, EventArgs.Empty);
                    }
                }
                catch (Exception ex)
                {
                    if (ex is WebException)
                    {
                        if (IsTry && ++_triedCount <= TryCount)
                        {
                            Thread.Sleep(TryInterval);

                            AsyncUpload(RemoteFileUri, LocalFilePath);

                            return;
                        }
                    }

                    Console.WriteLine(ex);

                    if (Aborted != null)
                    {
                        Aborted(this, EventArgs.Empty);
                    }
                }
            }
        }
    }
}
